using BootstrapBlazorApp.Server.LLMs.api;
using BootstrapBlazorApp.Server.LLMs.chain;
using BootstrapBlazorApp.Server.LLMs.messages;
using BootstrapBlazorApp.Server.LLMs.model.Kimi;
using BootstrapBlazorApp.Server.LLMs.model.TongYi;
using BootstrapBlazorApp.Server.LLMs.parser;
using BootstrapBlazorApp.Server.LLMs.prompt;
using BootstrapBlazorApp.Server.Utils;
using System.ComponentModel;

namespace BootstrapBlazorApp.Server.Service;

public class LlmChainService
{
    readonly TongYiConfig _config = new("sk-02112a6101224a7790d1efba39cbb3d3");
    readonly KimiConfig _kimiConfig = new("sk-FFBqRHmUD5EqIDJWfwH0xrfegU38rsGuwmN1JD6Z2lxFZ1IU");

    const string ScorePrompt = """
                               你是一个主观题评分专专家
                               请根据用户的文本回答和我提供的标准答案，给出评分和一个简短的总评。总评长度不要超过30字
                               你可以先使用工具来获取文本相似度。
                               问题：{question}
                               用户文本回答：{answer}
                               标准答案：{standardAnswer}
                               """;

    const string ExampleJson = """
                               [
                                   {"inputquestion": "HashMap 的长度为什么是 2 的幂次方？", 
                                   "inputanswer": "能更好地保证哈希值的均匀分布",
                                   "inputstandardAnswer": "1.位运算效率更高：位运算(&)比取余运算(%)更高效。
                                   当长度为 2 的幂次方时，hash % length 等价于 hash & (length - 1)。 
                                   2.可以更好地保证哈希值的均匀分布：
                                   扩容之后，在旧数组元素 hash 值比较均匀的情况下，新数组元素也会被分配的比较均匀，
                                   最好的情况是会有一半在新数组的前半部分，一半在新数组后半部分。
                                    3.扩容机制变得简单和高效：扩容后只需检查哈希值高位的变化来决定元素的新位置，要么位置不变（高位为 0），
                                    要么就是移动到新位置（高位为 1，原索引位置+原容量）。",
                                   "outputscore": "60",
                                   "outputcomment":"回答过于简短，只回答出了一个要点，还需要进一步掌握基础知识"},
                               ]

                               """;

    const string DetailPrompt = """
                                请根据用户的文本回答、我提供的标准答案、评分、一个简短的总评。给出一个详细评语
                                请你直接给出评语，不要包括其他任何上下文信息
                                问题：{question}
                                用户文本回答：{answer}
                                标准答案：{standardAnswer}
                                评分：{score}
                                总评：{comment}
                                """;

    public async Task<Dictionary<string, string>> ScoreChainAsync(string question, string answer, string standardAnswer,
        string model = "tongyi")
    {
        var promptTemplate = FewShotChatPromptTemplate.FromMessages(
        [
            new HumanMessage("""
                                问题：{inputquestion},
                                用户文本回答：{inputanswer},
                                标准答案：{inputstandardAnswer}
                             """),
            new AiMessage("""
                          {
                                score：{outputscore},
                                comment：{outputcomment}
                          }                               
                          """)
        ], ExampleJson);

        ChatPromptTemplate chatPromptTemplate = ChatPromptTemplate.FromMessages([
            new HumanMessage(ScorePrompt)
        ], promptTemplate);
        IBaseModel chatModel = model switch
        {
            "tongyi" => new ChatTongYi(_config),
            "kimi" => new ChatKimi(_kimiConfig),
            _ => throw new ArgumentOutOfRangeException(nameof(model), model, null)
        };

        chatModel.BindTools(typeof(SimilarityUtils));


        var chain = SequenceChain.Init(chatPromptTemplate)
            .Pipeline(chatModel)
            .Pipeline(new JsonDicOutPutParser(typeof(OutPutDic)));
        var chainContext =
            await chain.InvokeAsync(new Dictionary<string, string>
            {
                { "question", question }, { "answer", answer }, { "standardAnswer", standardAnswer }
            });

        return chainContext.OutPutContent;
    }

    public async IAsyncEnumerable<string> DetailChainStream(
        string question, string answer, string standardAnswer
        , Dictionary<string, string> input, string model = "tongyi")
    {
        IBaseModel chatModel = model switch
        {
            "tongyi" => new ChatTongYi(_config),
            "kimi" => new ChatKimi(_kimiConfig),
            _ => throw new ArgumentOutOfRangeException(nameof(model), model, null)
        };
        var promptTemplate = ChatPromptTemplate.GetPromptTemplate([
            new HumanMessage(DetailPrompt)
        ]);
        var chain = SequenceChain.Init(promptTemplate)
            .Pipeline(chatModel)
            .Pipeline(new StringOutPutParser());
        await foreach (var context in chain.InvokeStreamAsync(new Dictionary<string, string>
                       {
                           { "question", question },
                           { "answer", answer },
                           { "standardAnswer", standardAnswer },
                           { "score", input["score"] },
                           { "comment", input["comment"] }
                       }))
        {
            yield return context.OutPutContent;
        }
    }
}

class OutPutDic
{
    [Description("评分")] public string score { get; set; } = "100";
    [Description("总评")] public string comment { get; set; } = "回答的很好";
}
